useMmo 是用來優化性能的,他利用函式的回傳值來判斷元件是否需要重新渲染,可以用在比較複雜的邏輯處理上。它接受兩個參數,第一個是 callback,用來處理計算邏輯,第二個是 array,當 array 發生改變,useMemo 才會重新執行。useMemo 會有一個記憶值,這個記憶值就是 callback 的返回值,當 array 發生改變,才重新計算記憶值。
const memoizedValue = useMemo(callback, array)
下面是一個簡易的範例,我們在 TestComponent 中設了兩個屬性 countOne、countTwo,每次點擊 button 就會分別更新這兩個屬性,而 otherCount 應該要在 countOne 有變動時才跟著變動。但是實際運作中會發現,因為 React 的特性,只要每次 countOne 或 countTwo 更新,整個元件就會一起更新,所以 otherCount 也會一起更新,造成不必要的耗能。
const UserProfile = () => {
const [countOne, setCountOne] = useState(0)
const [countTwo, setCountTwo] = useState(0)
const otherCount = countOne + Math.random() // 每次都跟著一起跟新
return <div>
<button onClick={ () => setCountOne(countOne + 1)}>+1</button>
<button onClick={ () => setCountTwo(countTwo + 1)}>+1</button>
{ otherCount }
</div>
}
這時候就可以使用 useMemo 來返回一個記憶值給 otherCount,這個記憶值只有在 countOne 變動時才跟著變動。
const UserProfile = () => {
const [countOne, setCountOne] = useState(0)
const [countTwo, setCountTwo] = useState(0)
// const otherCount = countOne + Math.random()
const otherCount = useMemo(() => countOne + Math.random() , [countOne]) //countOne 變動才會改變
return <div>
<div>{countOne}</div>
<div>{countTwo}</div>
<button onClick={ () => setCountOne(countOne + 1)}>+1</button>
<button onClick={ () => setCountTwo(countTwo + 1)}>+1</button>
{ otherCount }
</div>
}
useCallback 和 useMemo 一樣是用於性能優化,一樣是有 callback 和 array 兩個參數,callback 也是處理計算邏輯,array 發生改變時,useCallback 才會執行。useCallback 的返回值也會記憶緩存起來,跟 useMemo 的差異處在於,useMemo 的返回值就是 callback 返回值,useCallback 的返回值是 callback 函式本身。
const memoizedCallback = useCallback(callback, array)
下面是一個和 useMemo 一樣的範例,但不同的是,我們用 useCallback 返回得到的函式。useCallback 和 useMemo 的使用時機,取決於產品的業務邏輯。
const UserProfile = () => {
const [countOne, setCountOne] = useState(0)
const [countTwo, setCountTwo] = useState(0)
const doSomeThing = (value) => {
const obj = {
value: value + 1
}
return obj
}
// useCallback 返回函式
const otherCount = useCallback(() => doSomeThing(countOne) , [countOne]) //countOne 變動才會改變
return <div>
<div>{countOne}</div>
<div>{countTwo}</div>
<button onClick={ () => setCountOne(countOne + 1)}>+1</button>
<button onClick={ () => setCountTwo(countTwo + 1)}>+1</button>
{ otherCount().value }
</div>
}
useRef 會返回一個可以改變的 ref 物件,這個物件的 current 屬性會被初始化為傳遞的參數。使用方式很簡單,下面的範例中,我們調用 useRef 並賦給 inputRef 一個初始值,接著給 input 一個 ref 屬性,並將 inputRef 賦給 ref,之後就可以透過 geInput 獲取 inputRef 中的值
const UserProfile = () => {
const inputRef = useRef(null)
const geInput = () => {
console.log(inputRef.current.value)
}
return <div>
<input ref={ inputRef } />
<button onClick={ geInput }>get input value</button>
</div>
}
useImperativeHandle 是 useRef 的拓展語法,它能讓我們把自定義的 ref 暴露給父元件。它接受兩個參數,第一個是 ref,第二個是函式,函式會返回一個物件給父元件
function Input(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
Input = forwardRef(FancyInput);
useLayoutEffect 和 useEffect 基本一樣,但不同之處是 useLayoutEffect 會在所有 DOM 更新之後同步觸發,可以使用它來讀取 DOM 布局並同步觸發重新渲染。一般情況下,應該盡量使用 useEffect,以免阻塞視覺的更新。useLayoutEffect 一樣是接受兩個參數,一個是函式,用來處理一些邏輯,一個是陣列,用來控制 useLayoutEffect 的執行。useLayoutEffect 因為用到的機會比較少,這邊就不多述,想了解更多可以閱讀 關於 useLayoutEffect,你需要知道的事 這篇文章。
useLayoutEffect(() => {
const rootElement = document.getElementById('root')
console.log(rootElement)
}, [])